package tcp

import (
	"crypto/tls"
	"fmt"
	"gitee.com/zhongguo168a/go-nodex/nodex"
	"gitee.com/zhongguo168a/go-nodex/nodex/models"
	"gitee.com/zhongguo168a/go-nodex/nodex/server"
	"gitee.com/zhongguo168a/go-nodex/nodex/server/msghandler"
	"gitee.com/zhongguo168a/gocodes/datax"
	"gitee.com/zhongguo168a/gocodes/myx/errorx"
	"github.com/dyson/certman"
	"log"
	"net"
	"os"
)

const NAME = "tcp"

func init() {
	nodex.RegisterServerCreator(NAME, NewServer)
}

/*
  创建一个服务器句柄
*/
func NewServer(conf *models.ServerConfig) server.IServer {
	s := &Server{
		name:      conf.Name,
		IPVersion: "tcp4",
		IP:        conf.Host,
		Port:      conf.Port,
		ConnMgr:   server.NewConnManager(),
		config:    conf,
	}
	handler := &msghandler.SocketMsgHandle{}
	handler.MsgHandle = *nodex.NewMsgHandle(s)
	s.SetMsgHandle(handler)
	return s
}

//iServer 接口实现，定义一个Server服务类
type Server struct {
	//服务器的名称
	name string
	//
	config *models.ServerConfig
	//tcp4 or other
	IPVersion string
	//服务绑定的IP地址
	IP string
	//服务绑定的端口
	Port string
	//当前Server的消息管理模块，用来绑定MsgId和对应的处理方法
	msgHandler server.IMsgHandle
	//当前Server的链接管理器
	ConnMgr server.IConnManager
	//该Server的连接创建时Hook函数
	OnConnStart func(conn server.IConnection)
	//该Server的连接断开时的Hook函数
	OnConnStop func(conn server.IConnection)
}

func (s *Server) AddRouter(msgId string, router server.IRouter) {
	s.msgHandler.AddRouter(msgId, router)
}

//============== 实现 ziface.IServer 里的全部接口方法 ========
func (s *Server) Name() string {
	return s.name
}

func (s *Server) GetConfig() *models.ServerConfig {
	return s.config
}

func (s *Server) SetMsgHandle(val server.IMsgHandle) {
	s.msgHandler = val
}

//开启网络服务
func (s *Server) Start() {
	fmt.Printf("[%v] 启动节点服务器, 监听地址: %v:%v\n", s.Name(), s.IP, s.Port)
	var (
		err error
	)
	defer func() {
		if err != nil {
			fmt.Println(errorx.Wrap(err, fmt.Sprintf("[%v] 启动失败", s.Name())))
		}
	}()

	//开启一个go去做服务端Linster业务
	if s.config.CertFile != "" || s.config.KeyFile != "" {
		err = s.listenerTLS()
	} else {
		err = s.listener()
	}
}

func (s *Server) listenerTLS() (err error) {
	cm, certerr := certman.New(s.config.CertFile, s.config.KeyFile)
	if certerr != nil {
		err = errorx.Wrap(certerr, "new certman")
		return
	}
	logger := log.New(os.Stdout, "", log.LstdFlags)
	cm.Logger(logger)
	if err = cm.Watch(); err != nil {
		err = errorx.Wrap(certerr, "watch certman")
		return
	}
	fmt.Printf("[%v] Use certman to watch ssl file", s.Name())

	config := &tls.Config{}
	config.NextProtos = []string{"http/1.1"}
	config.GetCertificate = cm.GetCertificate

	listener, err := tls.Listen("tcp", fmt.Sprintf("%v:%v", s.IP, s.Port), config)
	if err != nil {
		err = errorx.Wrap(err, "tls.Listen", datax.M{"ip": s.IP, "port": s.Port})
		return
	}
	//已经监听成功
	fmt.Printf("[%v] 开始监听端口\n", s.name)
	go func() {
		var cid uint64
		cid = 0
		for {
			conn, accepterr := listener.Accept()
			if accepterr != nil {
				fmt.Println(errorx.Wrap(accepterr, "listener.AcceptTCP", datax.M{"ip": s.IP, "port": s.Port}))
				continue
			}

			//3.2 设置服务器最大连接控制,如果超过最大连接，那么则关闭此新的连接
			if s.ConnMgr.Len() >= s.GetConfig().MaxConn {
				// todo: 关闭前可以发送一个错误信息
				_ = conn.Close()
				continue
			}

			//3.3 处理该新连接请求的 业务 方法， 此时应该有 handler 和 conn是绑定的
			cid++
			dealConn := NewConntion(s, conn, cid, s.msgHandler)

			//3.4 启动当前链接的处理业务
			go dealConn.Start()
		}
	}()

	return
}

func (s *Server) listener() (err error) {
	//1 获取一个TCP的Addr
	addr, err := net.ResolveTCPAddr(s.IPVersion, fmt.Sprintf("%s:%s", s.IP, s.Port))
	if err != nil {
		fmt.Println(errorx.Wrap(err, "net.ResolveTCPAddr", datax.M{"ip": s.IP, "port": s.Port}))
		return
	}

	//2 监听服务器地址
	listener, err := net.ListenTCP(s.IPVersion, addr)
	if err != nil {
		fmt.Println(errorx.Wrap(err, "net.ListenTCP", datax.M{"ip": s.IP, "port": s.Port}))
		return
	}

	//已经监听成功
	fmt.Printf("[%v] 开始监听端口\n", s.name)

	//3 启动server网络连接业务
	go func() {

		var cid uint64
		cid = 0

		for {
			//3.1 阻塞等待客户端建立连接请求
			conn, accepterr := listener.AcceptTCP()
			if accepterr != nil {
				fmt.Println(errorx.Wrap(accepterr, "listener.AcceptTCP", datax.M{"ip": s.IP, "port": s.Port}))
				continue
			}

			//3.2 设置服务器最大连接控制,如果超过最大连接，那么则关闭此新的连接
			if s.ConnMgr.Len() >= s.GetConfig().MaxConn {
				// todo: 关闭前可以发送一个错误信息
				_ = conn.Close()
				continue
			}

			//3.3 处理该新连接请求的 业务 方法， 此时应该有 handler 和 conn是绑定的
			cid++
			dealConn := NewConntion(s, conn, cid, s.msgHandler)

			//3.4 启动当前链接的处理业务
			go dealConn.Start()
		}
	}()

	return
}

// Stop 停止服务
func (s *Server) Stop() {
	fmt.Printf("[系统] 关闭节点服务器[%v]\n", s.Name())

	//将其他需要清理的连接信息或者其他信息 也要一并停止或者清理
	s.ConnMgr.ClearConn()
}

// Serve 运行服务
func (s *Server) Serve() {
	s.Start()

	//TODO Server.Serve() 是否在启动服务的时候 还要处理其他的事情呢 可以在这里添加

	//阻塞,否则主Go退出， listenner的go将会退出
	select {}
}

// GetConnMgr 得到链接管理
func (s *Server) GetConnMgr() server.IConnManager {
	return s.ConnMgr
}

// SetOnConnStart 设置该Server的连接创建时Hook函数
func (s *Server) SetOnConnStart(hookFunc func(server.IConnection)) {
	s.OnConnStart = hookFunc
}

// SetOnConnStop 设置该Server的连接断开时的Hook函数
func (s *Server) SetOnConnStop(hookFunc func(server.IConnection)) {
	s.OnConnStop = hookFunc
}

// CallOnConnStart 调用连接OnConnStart Hook函数
func (s *Server) CallOnConnStart(conn server.IConnection) {
	if s.OnConnStart != nil {
		fmt.Println("---> CallOnConnStart....")
		s.OnConnStart(conn)
	}
}

// CallOnConnStop 调用连接OnConnStop Hook函数
func (s *Server) CallOnConnStop(conn server.IConnection) {
	if s.OnConnStop != nil {
		fmt.Println("---> CallOnConnStop....")
		s.OnConnStop(conn)
	}
}
